using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Build.Utilities;
using UnityEngine;

namespace YooAsset.Editor.GUI
{
    [InitializeOnLoad]
    internal static class YooAssetAddressableAssetInspectorGUI
    {
        static YooAssetAddressableAssetInspectorGUI()
        {
            UnityEditor.Editor.finishedDefaultHeaderGUI += OnPostHeaderGUI;
        }

        [UnityEngine.TestTools.ExcludeFromCoverage]
        static void OnPostHeaderGUI(UnityEditor.Editor editor)
        {
            if (editor.targets.Length <= 0) return;
            // only display for the Prefab/Model importer not the displayed GameObjects
            if (editor.targets[0] is GameObject) return;

            string path = AssetDatabase.GetAssetPath(editor.targets[0]);

            if (!IsPathValidForEntry(path)) return;

            GUILayout.BeginHorizontal();
            var old = FindInYooAssets(Selection.activeObject);
            bool isInYoo = old != 0;
            bool preAddressPrevEnabledState0 = UnityEngine.GUI.enabled;
            if (old == 2)
            {
                UnityEngine.GUI.enabled = false;
            }
            var newTog = GUILayout.Toggle(isInYoo, "YooAsset Addressable", GUILayout.ExpandWidth(false));
            if (old == 2)
            {
                UnityEngine.GUI.enabled = preAddressPrevEnabledState0;
            }
            if (isInYoo != newTog)
            {
                if (newTog)
                {
                    AssetBundleCollector collector = new AssetBundleCollector();
                    collector.CollectorGUID =
                        AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject));
                    if(AssetBundleCollectorSettingData.Setting.Packages.Count <= 0)
                        AssetBundleCollectorSettingData.CreatePackage("DefaultPackage");
                    if(AssetBundleCollectorSettingData.Setting.Packages[0].Groups.Count <= 0)
                        AssetBundleCollectorSettingData.CreateGroup(AssetBundleCollectorSettingData.Setting.Packages[0], "Default Group");
                    AssetBundleCollectorSettingData.CreateCollector(
                        AssetBundleCollectorSettingData.Setting.Packages[0].Groups[0], collector);
                    AssetBundleCollectorSettingData.FixFile();
                }
                else
                {
                    RemoveYooAssetCollector(Selection.activeObject);
                    AssetBundleCollectorSettingData.FixFile();
                }
            }

            if (isInYoo)
            {
                bool preAddressPrevEnabledState = UnityEngine.GUI.enabled;
                UnityEngine.GUI.enabled = false;
                string address = path;
                EditorGUILayout.DelayedTextField(address, GUILayout.ExpandWidth(true));
                UnityEngine.GUI.enabled = preAddressPrevEnabledState;
            }
            GUILayout.EndHorizontal();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="asset"></param>
        /// <returns></returns>
        private static int FindInYooAssets(UnityEngine.Object asset)
        {
            string assetPath = AssetDatabase.GetAssetPath(asset);
            string guid = AssetDatabase.AssetPathToGUID(assetPath);
            foreach (var package in AssetBundleCollectorSettingData.Setting.Packages)
            {
                foreach (var group in package.Groups)
                {
                    foreach (var collector in group.Collectors)
                    {
                        if (collector.CollectorGUID == guid)
                        {
                            return 1;
                        }

                        if (assetPath.StartsWith(collector.CollectPath))
                        {
                            return 2;
                        }
                    }
                }
            }

            return 0;
        }

        private static bool RemoveYooAssetCollector(UnityEngine.Object asset)
        {
            string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(Selection.activeObject));
            foreach (var package in AssetBundleCollectorSettingData.Setting.Packages)
            {
                foreach (var group in package.Groups)
                {
                    foreach (var collector in group.Collectors)
                    {
                        if (collector.CollectorGUID == guid)
                        {
                            AssetBundleCollectorSettingData.RemoveCollector(group, collector);
                            return true;
                        }
                    }
                }
            }

            return false;
        }

        private static string isEditorFolder = $"{Path.DirectorySeparatorChar}Editor";
        private static string insideEditorFolder = $"{Path.DirectorySeparatorChar}Editor{Path.DirectorySeparatorChar}";
        static HashSet<string> excludedExtensions = new HashSet<string>(new string[]
            { ".cs", ".js", ".boo", ".exe", ".dll", ".meta", ".preset", ".asmdef" });

        internal static bool IsPathValidForEntry(string path)
        {
            if (string.IsNullOrEmpty(path))
                return false;

            if (path.Contains('\\'))
                path = path.Replace('\\', Path.DirectorySeparatorChar);

            if (Path.DirectorySeparatorChar != '/' && path.Contains('/'))
                path = path.Replace('/', Path.DirectorySeparatorChar);

            if (!path.StartsWith("Assets", StringComparison.OrdinalIgnoreCase) && !IsPathValidPackageAsset(path))
                return false;

            string ext = Path.GetExtension(path);
            if (string.IsNullOrEmpty(ext))
            {
                // is folder
                if (path == "Assets")
                    return false;
                int editorIndex = path.IndexOf(isEditorFolder, StringComparison.OrdinalIgnoreCase);
                if (editorIndex != -1)
                {
                    int length = path.Length;
                    if (editorIndex == length - 7)
                        return false;
                    if (path[editorIndex + 7] == '/')
                        return false;
                    // Could still have something like Assets/editorthings/Editor/things, but less likely
                    if (StringContains(path, insideEditorFolder, StringComparison.OrdinalIgnoreCase))
                        return false;
                }

                if (String.Equals(path, CommonStrings.UnityEditorResourcePath, StringComparison.Ordinal) ||
                    String.Equals(path, CommonStrings.UnityDefaultResourcePath, StringComparison.Ordinal) ||
                    String.Equals(path, CommonStrings.UnityBuiltInExtraPath, StringComparison.Ordinal))
                    return false;
            }
            else
            {
                // asset type
                if (StringContains(path, insideEditorFolder, StringComparison.OrdinalIgnoreCase))
                    return false;
                if (excludedExtensions.Contains(ext))
                    return false;
            }
            
            return true;
        }

        internal static bool IsPathValidPackageAsset(string pathLowerCase)
        {
            string[] splitPath = pathLowerCase.Split(Path.DirectorySeparatorChar);

            if (splitPath.Length < 3)
                return false;
            if (!String.Equals(splitPath[0], "packages", StringComparison.OrdinalIgnoreCase))
                return false;
            if (String.Equals(splitPath[2], "package.json", StringComparison.OrdinalIgnoreCase))
                return false;
            return true;
        }

        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        internal static bool StringContains(string input, string value, StringComparison comp)
        {
#if NET_UNITY_4_8
            return input.Contains(value, comp);
#else
            return input.Contains(value);
#endif
        }
    }
}
